package user

import (
	"context"
	"fmt"
	"strings"

	"mall/common/cryptx"
	"mall/common/globalkey"
	"mall/common/utils/pointy"
	"mall/service/backend/common/errorx"
	"mall/service/backend/model"
	"mall/service/backend/rpc/internal/svc"
	"mall/service/backend/rpc/internal/utils/errorhandler"
	"mall/service/backend/rpc/types/backend"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type LoginLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
	return &LoginLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *LoginLogic) Login(in *backend.LoginReq) (*backend.UserInfo, error) {
	if in.Account == nil || len(strings.TrimSpace(*in.Account)) == 0 {
		return nil, errorx.NewInvalidArgumentError("login.accountRequired")
	}

	if in.Password == nil || len(strings.TrimSpace(*in.Password)) == 0 {
		return nil, errorx.NewInvalidArgumentError("login.passwordRequired")
	}

	account := strings.TrimSpace(*in.Account)

	filterMap := make(map[string]map[string][]any)
	if in.Type != nil && len(strings.TrimSpace(*in.Type)) > 0 {
		switch strings.TrimSpace(*in.Type) {
		case globalkey.UserAuthTypeAccount:
			filterMap[model.SysUserColumns.Username] = map[string][]any{
				"equalTo": []any{account},
			}
		case globalkey.UserAuthTypeMobile:
			filterMap[model.SysUserColumns.Mobile] = map[string][]any{
				"equalTo": []any{account},
			}
		case globalkey.UserAuthTypeEmail:
			filterMap[model.SysUserColumns.Email] = map[string][]any{
				"equalTo": []any{account},
			}
		case globalkey.UserAuthTypeUsername:
			filterMap[model.SysUserColumns.Username] = map[string][]any{
				"equalTo": []any{account},
			}
		default:
			filterMap[model.SysUserColumns.Username] = map[string][]any{
				"equalTo": []any{account},
			}
		}
	}

	users, _, err := l.svcCtx.SysUser.GetList(l.ctx, model.M{
		"pageSize": uint64(1),
		"orderBy":  model.SysUserColumns.ID,
	}, -1, filterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if len(users) == 0 {
		return nil, errorx.NewInvalidArgumentError("login.wrongUsernameOrPassword")
	}

	user := users[0]

	if int32(user.Status) != globalkey.StatusEnabled {
		return nil, errorx.NewUnauthenticatedError("login.accountForbidden")
	}

	password := cryptx.EncryptPasswordWithSalt(strings.TrimSpace(*in.Password), user.Salt)
	if password != user.Password {
		logrus.Info(fmt.Sprintf("Login: %+v", password))
		return nil, errorx.NewInvalidArgumentError("login.wrongUsernameOrPassword")
	}

	var roleIds []uint64
	var roleCodes []string
	if in.WithRoles != nil && *in.WithRoles > 0 {
		preFilterMap := make(map[string]map[string][]any)
		preFilterMap[model.SysUserRoleRelationshipColumns.UserID] = map[string][]any{
			"equalTo": []any{user.ID},
		}
		rel, _, err := l.svcCtx.SysUser.GetRelRoleList(l.ctx, model.M{}, -1, preFilterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		var rids []any
		for _, v := range rel {
			rids = append(rids, v.RoleID)
		}
		if len(rids) > 0 {
			roleFilterMap := make(map[string]map[string][]any)
			roleFilterMap[model.SysRoleColumns.ID] = map[string][]any{
				"in": rids,
			}
			roleFilterMap[model.SysRoleColumns.Status] = map[string][]any{
				"equalTo": []any{globalkey.StatusEnabled},
			}
			roles, _, err := l.svcCtx.SysRole.GetList(l.ctx, model.M{
				"select": model.SysRoleColumns.ID + "," + model.SysRoleColumns.Code,
			}, -1, roleFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			for _, v := range roles {
				roleIds = append(roleIds, v.ID)
				roleCodes = append(roleCodes, v.Code)
			}
		}
	}

	return &backend.UserInfo{
		Id:        pointy.GetPointer(user.ID),
		RoleIds:   roleIds,
		RoleCodes: roleCodes,
	}, nil
}
